/********************************/
//  CORE Javascript Library
/********************************/

// Greg Houldsworth
// Aaron Murray


(function(window){ //create a private scope



  /***************************************************************/
  //if they don't exist, add the "forEach", "every" and "filter" methods to all Arrays using prototype
  //(these methods were added in Javascript 1.6, so IE7  and lower doesn't support them)
  if(!Array.prototype.forEach){
    Array.prototype.forEach = function(fn, thisObj){
      var scope = thisObj || window;
      for( var i=0, j=this.length; i < j; ++i ){
        fn.call(scope, this[i], i, this);
      }
    };
  }
  if(!Array.prototype.every){
    Array.prototype.every = function(fn, thisObj){
      var scope = thisObj || window;
      for( var i=0, j=this.length; i < j; ++i ){
        if(i in this && !fn.call(scope, this[i], i, this)){
          return false;
        }
      }
      return true;
    };
  }
  if(!Array.prototype.filter){
    Array.prototype.filter = function(fn, thisObj){
      var scope = thisObj || window;
      var a = [];
      for( var i=0, j=this.length; i < j; ++i ){
        if( !fn.call(scope, this[i], i, this) ){
          continue;
        }
        a.push(this[i]);
      }
      return a;
    };
  }


  function log(){
    try{
      return console.log.apply(console, arguments);
    }
    catch(e){}
  }

  var undefined,
      document = window.document,
      push     = Array.prototype.push,
      forEach  = Array.prototype.forEach,
      filter   = Array.prototype.filter,
      slice    = Array.prototype.slice,
      splice    = Array.prototype.splice,
      concat   = Array.prototype.concat;

      
  //export core object to the global namespace but keep an internal reference as well
  var core = window.core = {
    "classes": {},
    "subclass": function(parentClass, objProtoExt, arParentArgs){
      function subClass(){
        //execute the default constructor in the context of our new object
        parentClass.call(this);
        //if the subClass has or inherited an init method, execute it with
        //any arguments passed to this consructor
        if(this.init){
          this.init.apply(this, arguments)
        }
      }
      //subClass.prototype = new parentClass();
      subClass.prototype = core.newApply(parentClass, arParentArgs)
      this.augment(subClass, objProtoExt);
      subClass.prototype.constructor = subClass;
      subClass.prototype.parentConstructor = parentClass;
      return subClass;
    },
    
    "newApply": function(fnConstructor, arArguments){
      var s='';
      for(var i=0,l=(arArguments ? arArguments.length : 0); i<l; i++){ s += (i==0 ? '' : ',') + 'a[' + i + ']'; }
      return new (new Function("f", "a", 'return new f(' + s + ');'))(fnConstructor, arArguments);
    },
    
    "augment": function(fn, obj){
      if(obj){
        for(var key in obj){
          if(obj.hasOwnProperty(key)){
            fn.prototype[key] = obj[key];
          }
        }
      }
    },
    
    "dom": function(){
      return core.newApply(core.classes.DOMSelection, arguments);
      //return new core.classes.DOMSelection(criteria, context);
    },
    
    "merge": function(){
      //improve performance by applying the Array.prototype.concat method 
      var obj = arguments[0];
      for(var j=1, jl=arguments.length; j<jl; j++){
        var objToAdd = arguments[j];
        if(objToAdd && objToAdd.length){
          for(var i=0, l=objToAdd.length; i<l; i++){
            push.call(obj, objToAdd[i]);
          }
        }
      }
      return obj;
    },
    
    /***************************************************************/
    //toArray 
    //takes an object with 'length' property and returns an actual array
    //(useful for 'arguments' and objects returned from getElementsByTagName(), etc..)
    "toArray": function(){
      //try blocks aren't too bad on performance as long as they don't have to handle an error
      try{
        //can't get this approach to work in ie (DOM nodelists throw a "Jscript object expected" error)
        //return [].slice.call(objObject, 0);
        var arReturn = slice.call(arguments[0], 0);
        //append the indexes of any more argument objects 
        for(var i=1,l=arguments.length; i<l; i++){
          arReturn = arReturn.concat(slice.call(arguments[i], 0));
        }
        return arReturn;
      }
      catch(e){
        //reassign the function as the slower manual version 
        core.toArray = function(){
          var arReturn = []. obj;
          for(var i=0, il=arguments.length; i<il; i++){
            obj = arguments[i];
            for(var j=0, jl=obj.length; j<jl; j++){
              arReturn[arReturn.length] = obj[j];
            }
          }
          return arReturn;
        };
        //then re-run with the new function
        return core.toArray.apply(null, arguments);
      }
    },
    
    "isArray": isArray,
    "isString": isString,
    "isObject": isObject,
    "isFunction": isFunction
  };
  
  
  /*-----------------------------------------------*/
  /*-----------------COLLECTION--------------------*/
  /*-----------------------------------------------*/
  core.classes.Collection = function(){
    this.length = 0;
    this.push.apply(this, arguments);
  };
  core.classes.Collection.prototype = {
    "toString": function(){ return Object.prototype.toString.call(this); },
    "push":     push,
    "forEach":  forEach,
    "splice":   splice,
    "augment": function(obj){
      core.augment(this.constructor, obj);
      return this;
    },
    "add": function(){
      push.apply(this, arguments);
      return this;
    },
    "filter": function(){
      return new this.constructor(filter.call(this));
    },
    "merge": function(){
      var a = [this];
      for(var i=0, l=arguments.length; i<l; i++){
        a[i+1] = arguments[i];
      }
      return core.merge.apply(this, a);
    },
    "normalize": function(){
      for(var i=0; i<this.length-1; i++){
        for(var j=i+1; j<this.length; j++){
          if(this[j]===this[i]){
            this[j] = undefined;
            this.splice(j, 1);
            j--;
          }
        }
      }
      return this;
    },
    "index": function(ind){
      //extra check to make sure the index is below the length property
      return new this.constructor((ind < this.length ? this[ind] : null), this.selectionContext);
    },
    "first": function(){ return this.index(0); },
    "last": function(){ return this.index(this.length-1); },
    "contains": function(){
      var args = arguments;
      var objColl = this.filter(function(obj){
        for(var i=0, l=args.length; i<l; i++){
          if(obj === args[i]){
            return true;
          }
        }
        return false;
      });
      args = null;
      return objColl.length ? objColl : null;
    },
    "empty": function(){ return this.setLength(0); },
    "setLength": function(newLength){
      var i = this.length;
      while(i > newLength-1){
        this[i] = undefined;
        delete this[i];
        //log('x', i);
        i--;
      } 
      this.length = newLength;
      return this;
    },
    "DOMElements": function(){
      return new this.constructor(this.filter(function(obj){ return !!(obj.nodeType && obj.nodeType!=8); }));
    },
    "toArray": function(){ return core.toArray(this); },
    "constructor": core.classes.Collection
  };
  
  
  
  
  
  /*-----------------------------------------------*/
  /*----------------DOM SELECTION------------------*/
  /*-----------------------------------------------*/
  
  core.classes.DOMSelection = core.subclass(
    core.classes.Collection,
    {
      "init": function(criteria, selectionContext){
        this.length = 0;
        this.selectionContext = this.resolveContext(selectionContext, document) || document;
        if(arguments.length){
          this.add.apply(this, arguments);
        }
      },
      
      "debug": function(str){
        log((new Date).getTime() + ': ' + str);
        this.forEach(function(el){ log(el); });
        return this;
      },

      
      "toString" : function(){
        //return this.merge.apply([], this).toString();
        return '[object DOMSelection]';
      },
      
      
      "add": function(criteria, selectionContext){
        var arg,
            sc = this.resolveContext(selectionContext) || this.selectionContext;
            
        if(criteria){
          criteria = isArray(criteria) || criteria instanceof core.classes.DOMSelection ? criteria : [criteria];
          for(var i=0, l=criteria.length; i<l; i++){
            arg = criteria[i];
            if(isString(arg)){
              this.merge(this.resolve(arg, sc), sc);
            }
            else if(arg.nodeType && arg.nodeType){
              this.push(arg);
            }
          }
        }
        //remove duplicates
        return this.normalize();
      },
      
      "merge": function(){
        //apply the merge method that would have been inherited from the parent class and then normalize the collection
        core.classes.Collection.prototype.merge.apply(this, arguments);
        return core.classes.DOMSelection.prototype.normalize.call(this);
      },
  
      "resolve": function(str, sc, arr){
        return Sizzle(str, sc, arr);
        /*
        var ar = str.split(' '),
            arRet = [];
        sc = sc || document;
        for(var i=0, l=ar.length; i<l; i++){
          var firstChar = ar[i].substr(0, 1);
          if(firstChar == '.'){
            this.merge.call(arRet, getElementsByClassName(ar[i].substr(1), sc));
          }
          else if(firstChar == '#' || firstChar == '@'){
            this.merge.call(arRet, getChildElementById(sc, ar[i].substr(1), firstChar == '@'));
          }
          else{
            this.merge.call(arRet, getElementsByTagName(ar[i], sc));
          }
        }
        return arRet;
        */
      },
      
      "find": function(criteria){
        var arRet = findIndexes(this, criteria),
            ts    = this;
        arRet.forEach(function(ind, i){ arRet[i] = ts[ind]; });
        ts = null;
        return new this.constructor(arRet, this.selectionContext);
      },
      
      "drop": function(criteria){
        var ar = findIndexes(this, criteria),
            l = ar.length,
            i = l;
        while(i--){
          splice.call(this, ar[i], 1);
        }
        return this;
      },
      
      "wait": function(msec, fn){
        var ts = this;
        window.setTimeout(function(){ fn.call(ts); ts = null; }, msec);
        return this;
      },
      
      "append": function(parent){
        if(isString(parent)){
          parent = this.resolve(parent, document)[0];
        }
        var fr = document.createDocumentFragment();
        this.forEach(function(el){ fr.appendChild(el); });
        parent.appendChild(fr);
      },
      
      "remove": function(){
        this.forEach(function(el){
          if(el && el.parentNode){
            el.parentNode.removeChild(el);
          }
        });
        return this;
      },
      
      "show": function(){
        this.forEach(function(el){ el.style.display = '';});
        return this;
      },
      
      "hide": function(){
        this.forEach(function(el){ el.style.display = 'none';});
        return this;
      },
      
      "resolveContext": function(selectionContext){
        var sc;
        if(selectionContext){
          if(isString(selectionContext)){
            var tmpEl = this.resolve(selectionContext, document)[0];
            if(tmpEl && tmpEl.nodeType){
              sc = tmpEl;
            }
          }
          else if(selectionContext.nodeType){
            sc = selectionContext;
          }
          else if(selectionContext[0] && selectionContext[0].nodeType){
            sc = selectionContext[0];
          }
        }
        return (sc && sc.nodeType ? sc : null);
      }
    }
  );
  
  
  function findIndexes(arToSearch, criteria){
    var arMatches = Sizzle.matches(criteria, arToSearch),
        arIndexes = [],
        il = arMatches.length,
        i,
        jl = arToSearch.length,
        j;
    
    for(i=0; i<il; i++){
      for(j=0; j< jl; j++){
        if(arMatches[i] === arToSearch[j]){
          arIndexes.push(j);
        }
      }
    }
    return arIndexes;
    /*
    var ar    = isString(criteria) ? criteria.split(' ') : (isArray(criteria) ? criteria : [criteria]),
        arRet = [],
        jl    = arToSearch.length,
        firstChar;
    for(var i=0, l=ar.length; i<l; i++){
      firstChar = (ar[i] && ar[i].substr) ? ar[i].substr(0,1) : null;
      for(var j=0; j< jl; j++){
        if(ar[i] && (
            (ar[i] === arToSearch[j]) ||
            (firstChar == '.' && hasClass(arToSearch[j], ar[i].substr(1))) ||
            (firstChar == '#' && arToSearch[j].getAttribute('id') == ar[i].substr(1)) ||
            (ar[i] == '*' || ar[i].toLowerCase() == arToSearch[j].tagName.toLowerCase())
          )){
          arRet[arRet.length] = j;
        }
      }
    }
    return arRet;
    */
  }
  
  
  /*
  //example of an extension of the DOMSelection class
  core.classes.DIVSelection = core.subclass(
    core.classes.DOMSelection,
    {
      "init": function(){
        this.amDivs = true;
      },
      "add": function(){
        this.parentConstructor.prototype.add.apply(this, arguments);
        var o = this.find("div").toArray();
        this.empty().merge(o);
        return this;
      }
      
    }
  );
  
  window.objDIVSelection = new core.classes.DIVSelection;
  */
  
  
  
  
  
  function getElementsByTagName(strTagName, parentNode){
    parentNode = parentNode || document;
    if(parentNode.getElementsByTagName){
      return core.toArray(parentNode.getElementsByTagName(strTagName));
    }
    //if the element doesn't support getElementsByTagname
    return core.toArray(document.getElementsByTagName(strTagName)).filter(function(el){ return isChild(el, parentNode)});
  }
  
  
  /***************************************************************/
  /***************************************************************/
  /***************************************************************/
  
  //----------BELOW IS BOILERPLATE CODE COPIED FROM DDP------------
  
  /***************************************************************/
  /***************************************************************/
  /***************************************************************/

  
  
  /***************************************************************/
  //isArray 
  //tests for arrays
  function isArray(varTest){
    if(varTest === null){
      return false;
    }
    if(typeof varTest == 'object' && typeof varTest.constructor == 'function' && typeof varTest.length == 'number' ){
      //return (varTest.constructor.toString().match(/function array/i) !== null);
      return !!varTest.constructor.toString().match(/function array/i);
    }

    return false;
  }
    
  
  /***************************************************************/
  //isString 
  //tests for strings and string objects
  function isString(varTest){
    if(varTest === null){
      return false;
    }
    if(typeof varTest == 'string'){
      return true;
    }
    if(typeof varTest == 'object' && typeof varTest.constructor == 'function'){
      //return (varTest.constructor.toString().match(/string/i) != null);
      return !!varTest.constructor.toString().match(/string/i);
    }
    return false;
  }
  
  
  /***************************************************************/
  //isObject
  //tests for objects which are not strings or arrays (and not null)
  function isObject(varTest){
    //it's not null, has typeof "object", isn't a string, isn't an array, and isn't a function
    return (varTest !== null && typeof varTest == 'object' && !isString(varTest) && !isArray(varTest) && !isFunction(varTest));
  }
  
  /***************************************************************/
  //isFunction
  //tests for objects which are not strings or arrays (and not null)
  function isFunction(varTest){
    if(!varTest){
      return false;
    }
    if(typeof varTest == 'function'){
      return true;
    }
    //ie6's stupid broken return value for functions across windows is "object"
    if(typeof varTest == 'object' && typeof varTest.constructor == 'object'){
      //return (varTest.constructor.toString().match(/function Function/) != null);
      return !!varTest.constructor.toString().match(/function Function/);
    }
    return false;
  }
  
  function isDate(varTest){
    return !!(varTest && varTest.constructor && varTest.constructor.toString().match(/Date/));
  }
  
  

  
  
  
//***************************************************************
//getElementsByClassName
//find elements underneath a parent element by their
//classname and return them in an array 
function getElementsByClassName(strClassName, elStartElement, strTagName) {
  if(!elStartElement)
    elStartElement = document;
    //elStartElement = document.getElementsByTagName('body')[0];
    
  if(!strTagName)
    strTagName = '*';
  
  if(elStartElement){
    //try to use the native getElementsByClassName as it's much faster (supported by FF3/webkit engine)
    if(elStartElement.getElementsByClassName){ 
      return core.toArray(elStartElement.getElementsByClassName(strClassName));
    }
    //then try to use the native querySelectorAll as it's also much faster (supported by FF3.5 and is a w3c standard)
    else if(elStartElement.querySelectorAll){
      return core.toArray(elStartElement.querySelectorAll('.' + strClassName));
    }
    //then default to doing it manually
    else{
      var arElements = []; //create array to return the elements in
      var arAllElements = null;
      //just get all sub-elements of the start element
      if(strTagName=='*' && elStartElement.all){
        arAllElements = elStartElement.all;
      }
      //if there are no spaces in the strTagName (only one tag)
      else if(strTagName.indexOf(' ') < 0){
        //use the getElementsByTagName function on the element
        arAllElements = elStartElement.getElementsByTagName(strTagName);
      }
      //multiple tag names
      else{
        var arTagNames = strTagName.split(' ');
        var arNodeLists = [];

        for(var i=0, l=arTagNames.length; i<l; i++){
          arNodeLists.push(elStartElement.getElementsByTagName(arTagNames[i]));
        }

        arAllElements = core.toArray.apply(null, arNodeLists);
      }
      
      for(var i=0, l=arAllElements.length; i<l; i++){
        if(hasClass(arAllElements[i], strClassName)){
          arElements.push(arAllElements[i]);
        }
      }
      
      return arElements;
    }
  }
  
  return null;
}




//***************************************************************
//getChildElementById 
//find an element by it's id, which is a child element of "elFromElement"
//returns the element's reference, or null if the element id can't be found 
function getChildElementById(elFromElement, idToFind, findMultipleIds){
  var returnValue = findMultipleIds ? [] : null;
  if(!elFromElement)
    elFromElement = document;
  
  if(!findMultipleIds && isString(idToFind)){
    var tmp = document.getElementById(idToFind);
    if(tmp && isChild(tmp, elFromElement)){
      return [tmp];
    }
  }
  
  if(elFromElement && elFromElement.getElementsByTagName){
    if(isArray(idToFind)){
      return getChildElementsByIds(elFromElement, idToFind, findMultipleIds);
    }
    var arChildElements = elFromElement.getElementsByTagName('*');
    var i = arChildElements.length;
    while(i--){
      if(arChildElements[i].id && arChildElements[i].id == idToFind){
        if(!findMultipleIds){
          return arChildElements[i];
        }
        else{
          returnValue[returnValue.length] = arChildElements[i];
        }
      }
    }
  }
  return returnValue;
}


//***************************************************************
//getChildElementsByIds
//finds multiple elements by their ids, which are child elements of "elFromElement"
//returns an object of element references, or null if the element id can't be found
//(this can save a lot of time vs. mutiple getChildElementById() calls as the whole
//DOM structure doesn't have to be search each time)
function getChildElementsByIds(elFromElement, arIdsToFind, findMultipleIds){
  var objReturn = {};
  //if we are going to splice elements out when we find them
  if(!findMultipleIds)
    //take a copy of the array
    arIdsToFind = core.toArray(arIdsToFind);
  //record the length so we can reset the decrementer without going back to the length property
  var idsLength = arIdsToFind.length;
  
  //get all elements under the "from element"
  var arChildElements = elFromElement.getElementsByTagName('*');
  var i = arChildElements.length;
  
  //loop backwards through the elements
  while(i--){
    var j = idsLength; //set the decrementer (or reset it)
    //loop backwards through the ids that were provided
    while(j--){
      //there is a match on the id we're looking for
      if(arChildElements[i].id && arChildElements[i].id == arIdsToFind[j]){
        //if we're searching for multiple elements with the same id within this DOM structure
        if(findMultipleIds){
          //the property doesn't exist yet
          if(typeof objReturn[arIdsToFind[j]] == 'undefined')
            objReturn[arIdsToFind[j]] = arChildElements[i]; //assign the element reference directly
          //it's already an array
          else if(isArray(objReturn[arIdsToFind[j]]))
            objReturn[arIdsToFind[j]].push(arChildElements[i]); //add another element
          //it's just a single element, no array
          else if(objReturn[arIdsToFind[j]])
            //make a new array with the current element being the first index and the new element the second
            objReturn[arIdsToFind[j]] = [objReturn[arIdsToFind[j]], arChildElements[i]];
        }
        else{
          //since we're looping backwards through the elements,
          //if the return object property gets overwritten,
          //it will be with the "first" occurance of the id.
          objReturn[arIdsToFind[j]] = arChildElements[i];
          arIdsToFind.splice(j, 1); //splice out the id we found so we don't have to search for it again.
          j=0; //we're not looking for multiple ids, so don't look for any more matches this round
        }
      }
    }
  }
  
  return objReturn;
}



//***************************************************************
//isChild 
//returns true if the first element supplied is a DOM descendent of the second element supplied, otherwise returns false.
function isChild(elChild, elParent, blnBridgeIframes){
  if(typeof elChild != 'object' || typeof elParent != 'object' || !elChild || !elParent){
    return null;
  }
  //if the browser supports the "contains" method (basically everyone but Firefox)
  if(elParent.contains){
    return elParent.contains(elChild);
  }
  //Firefox
  if(elChild.compareDocumentPosition){
    return Boolean(elParent.compareDocumentPosition(elChild) & 16); //single & is the bitwise AND operator, not a typo (compareDocumentPosition returns a bitmask).
  }
  //fallback to calculating it ourselves
  return (climbDOMTree(elChild, function(nodes, elParent){ if(this==elParent) return true; }, [elParent], blnBridgeIframes) === true);
}




//***************************************************************
//climbDOMTree
//climbs up the DOM tree one node at a time, starting from the supplied element "elStart"
//with each level, fnEvaluate is executed (with it's "this" value being the currently evaluated node)
//the first argument passed to fnEvaluate will be an array of the previously evaluated nodes, followed by 
//any arguments passed in the arArguments array 
function climbDOMTree(elStart, fnEvaluate, arArguments, blnBridgeIframes){
  var nodesClimbed = [];
  if(isArray(arArguments))
    arArguments.splice(0, 0, nodesClimbed); //slip a zero into the first index
  else
    arArguments = [nodesClimbed];

  for(var elCurrent = elStart; elCurrent; elCurrent = elCurrent.parentNode){
    //if we are allowing this climb to escape from the containing iframe (if there is one)
    if(blnBridgeIframes){
      //check if the node is a DOCUMENT_NODE
      if(elCurrent.nodeType == nodeTypes.DOCUMENT_NODE){
        var objParentWindow = null;
        //find the element's window's parent window 
        if(elCurrent.defaultView && elCurrent.defaultView.parent) //NS / FF
          objParentWindow = elCurrent.defaultView.parent;
        else if(elCurrent.parentWindow && elCurrent.parentWindow.parent) //IE
          objParentWindow = elCurrent.parentWindow.parent;
        else
          return null; //there is no parent window, so we're done
          
        //search through the parent's Iframes to find this element's document
        var elIframes = objParentWindow.document.getElementsByTagName('IFRAME');
        for(var i=0, l=elIframes.length;i<l;i++){
          //catch the error when we try to access the document object for other domains (FF) 
          try{
            //if the iframe's document accessed from the parent window is our document 
            if(elIframes[i].contentWindow.document == elCurrent)
              elCurrent = elIframes[i]; //then start climbing again at the iframe element of the parent window
          }
          catch(e){} //don't need to do anything 'cause if it's not in the same domain, it's not this document anyway
        }
      }
    }
    
    nodesClimbed.push(elCurrent);
    var retVal = fnEvaluate.apply(elCurrent, arArguments);
    if(typeof retVal != 'undefined')
      return retVal;
  }
  return null;
}




//***************************************************************
//private method. not exported to the public namespace
function _getCSSClass(el){
  //old school method, doesn't work in ie8 (technically that's good as it's not standards compliant)
  //return el.className;
  if(!el || !el.getAttribute){ //if an element wasn't passed, or it doesn't have a getAttribute method
    return null; //return null...
  }
    
  //if classname is null, use class. if class is null as well, return an empty string
  return el.getAttribute('className') || el.getAttribute('class') || '';
}


//***************************************************************
//private method. not exported to the public namespace
function _setCSSClass(el, strClassName){
  //old school method, doesn't work in ie8 (technically that's good as it's not standards compliant)
  if(el){
    return el.className = strClassName;
  }
  /*
  //check that the element isn't null and has the setAttribute() method
  if(el && el.setAttribute){
    return el.setAttribute('className', strClassName) || el.setAttribute('class', strClassName);
  }
  */
  return false;
}


//***************************************************************
//addClass
//adds a CSS class to the element's className attribute (if it's not already there)
//accepts a reference to an element and a string containing the class name
function addClass(elementOrArray, strClassToAdd){
  //if an array of elements was passed in, just set ar equal to it, otherwise, make an array with a single element
  var ar = (typeof elementOrArray.length != 'undefined' && typeof elementOrArray.nodeType == 'undefined') ? elementOrArray : [elementOrArray],
      i = ar.length,
      el,
      strClassName;
  
  while(i--){
    el = ar[i];
    //if the element passed is valid and the class isn't already there
    if(el && !hasClass(el, strClassToAdd)){
      strClassName = _getCSSClass(el) || '';
      _setCSSClass(el, strClassName.length ? strClassToAdd + ' ' + strClassName : strClassToAdd);
    }
  }
  return elementOrArray;
}


//***************************************************************
//removeClass
//removes a CSS class from the element's class attribute
//accepts a reference to an element and a string containing the class name
function removeClass(elementOrArray, classToRemove){
  //if an array of elements was passed in, just set ar equal to it, otherwise, make an array with a single element
  var ar = (typeof elementOrArray.length != 'undefined' && typeof elementOrArray.nodeType == 'undefined') ? elementOrArray : [elementOrArray],
      i = ar.length,
      el,
      strClass,
      re;
  
  while(i--){
    el = ar[i];
    strClass = _getCSSClass(el);
    if(el && strClass){
      //classToRemove = ddp.f.trimWhitespace(classToRemove);
      re = getRegExp('removeClass_re1' + classToRemove) || addRegExp('removeClass_re1' + classToRemove, new RegExp('\\b' + classToRemove + '\\b', 'g'));
      //var re = new RegExp('\\b' + classToRemove + '\\b', 'g');
      //el.className = trimWhitespace(el.className.replace(re, ''));  //remove the css class
      
      //_setCSSClass(el, trimWhitespace(strClass.replace(re, '')));  //remove the css class
      _setCSSClass(el, strClass.replace(re, ''));  //remove the css class
    }
  }
  return elementOrArray;
}


//***************************************************************
//hasClass
//accepts a reference to an element and string containing the class name to search for.
//returns true if the element has the specified class name, otherwise returns false.
function hasClass(elementOrArray, strClassToCheckFor){
  //if an array of elements was passed in, just set ar equal to it, otherwise, make an array with a single element
  var ar = (typeof elementOrArray.length != 'undefined' && typeof elementOrArray.nodeType == 'undefined') ? elementOrArray : [elementOrArray],
      l = ar.length,
      arReturn = [],
      i, el, strClass, re;
  
  for(i=0; i<l; i++){
    el = ar[i];
    if(el && (strClass = _getCSSClass(el))){
      //strClassToCheckFor = ddp.f.trimWhitespace(strClassToCheckFor);
      re = getRegExp('hasClass_re1_' + strClassToCheckFor) || addRegExp('hasClass_re1' + strClassToCheckFor, new RegExp('\\b' + strClassToCheckFor + '\\b', 'gi'));
      //return re.test(el.className);
      arReturn.push(re.test(strClass));
    }
    else{
      arReturn.push(false);
    }
  }

  return (arReturn.length==1) ? arReturn[0] : arReturn;
}


//***************************************************************
//removeClassAll
//removes a CSS class from an element and it's sub elements on the DOM.
//accepts a reference to an element, a string containing the class name, and a flag to indicate if
//the element should be detached from the DOM while the class is removed from children
function removeClassAll(elStartElement, classToRemove, detachWhileProcessing){
  //if elStartElement is not an element, getElementsByClassname will assume you meant the entire document
  var arEls = getElementsByClassname(elStartElement, classToRemove);
  arEls[els.length] = elStartElement;
  if(detachWhileProcessing && arEls.length > 1){
    var previousSibling = elStartElement.previousSibling;
    var parentNode = elStartElement.parentNode;
    detachElement(elStartElement);
  }
  arEls.forEach(function(thisElement){ removeClass(thisElement, classToRemove); });
  if(previousSibling){
    insertElementAfter(previousSibling, elStartElement);
  }
  else if(parentNode){
    parentNode.appendChild(elStartElement);
  }
  return arEls.length; //return the number of elements processed
}


/*-----------------
Regular Expression Caching
have yet to determine how much, if any, of a perfomance difference this makes
need to do some benchmarking. 
-----------------*/
var regExpCache = [];
regExpCache.itemsByName = {};

//ddp.f.ref = regExpCache;

function getRegExp(reName){
  if(regExpCache.itemsByName[reName]){
    if(regExpCache.length > 1){
      var i = getRegExpCacheIndex(reName);
      if(i<regExpCache.length){
        var tmp = regExpCache[i+1];
        regExpCache[i+1] = regExpCache[i];
        regExpCache[i] = tmp;
        tmp = null;
      }
    }
    return regExpCache.itemsByName[reName];
  }
  return null;
}

function addRegExp(reName, objRE){
  //if it exists (or one by the same name exists)
  if(regExpCache.itemsByName[reName]){
    //if it's exactly the same object
    if(regExpCache.itemsByName[reName] == objRE){
      return objRE;
    }
    else{
      regExpCache.splice(getRegExpCacheIndex(reName),1);
    }
  }
  
  if(regExpCache.length > 200){
    delete regExpCache.itemsByName[regExpCache.shift()];
  }

  regExpCache.push(reName);
  return (regExpCache.itemsByName[reName] = objRE);
}

function getRegExpCacheIndex(reName){
  var i = regExpCache.length;
  while(--i){
    if(regExpCache[i] == reName)
      return i;
  }
  return null;
}













/*!
 * Sizzle CSS Selector Engine
 *  Copyright 2011, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
	done = 0,
	toString = Object.prototype.toString,
	hasDuplicate = false,
	baseHasDuplicate = true,
	rBackslash = /\\/g,
	rNonWord = /\W/;

// Here we check if the JavaScript engine is using some sort of
// optimization where it does not always call our comparision
// function. If that is the case, discard the hasDuplicate value.
//   Thus far that includes Google Chrome.
[0, 0].sort(function() {
	baseHasDuplicate = false;
	return 0;
});

var Sizzle = function( selector, context, results, seed ) {
	results = results || [];
	context = context || document;

	var origContext = context;

	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
		return [];
	}
	
	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	var m, set, checkSet, extra, ret, cur, pop, i,
		prune = true,
		contextXML = Sizzle.isXML( context ),
		parts = [],
		soFar = selector;
	
	// Reset the position of the chunker regexp (start from head)
	do {
		chunker.exec( "" );
		m = chunker.exec( soFar );

		if ( m ) {
			soFar = m[3];
		
			parts.push( m[1] );
		
			if ( m[2] ) {
				extra = m[3];
				break;
			}
		}
	} while ( m );

	if ( parts.length > 1 && origPOS.exec( selector ) ) {

		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
			set = posProcess( parts[0] + parts[1], context );

		} else {
			set = Expr.relative[ parts[0] ] ?
				[ context ] :
				Sizzle( parts.shift(), context );

			while ( parts.length ) {
				selector = parts.shift();

				if ( Expr.relative[ selector ] ) {
					selector += parts.shift();
				}
				
				set = posProcess( selector, set );
			}
		}

	} else {
		// Take a shortcut and set the context if the root selector is an ID
		// (but not if it'll be faster if the inner selector is an ID)
		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {

			ret = Sizzle.find( parts.shift(), context, contextXML );
			context = ret.expr ?
				Sizzle.filter( ret.expr, ret.set )[0] :
				ret.set[0];
		}

		if ( context ) {
			ret = seed ?
				{ expr: parts.pop(), set: makeArray(seed) } :
				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );

			set = ret.expr ?
				Sizzle.filter( ret.expr, ret.set ) :
				ret.set;

			if ( parts.length > 0 ) {
				checkSet = makeArray( set );

			} else {
				prune = false;
			}

			while ( parts.length ) {
				cur = parts.pop();
				pop = cur;

				if ( !Expr.relative[ cur ] ) {
					cur = "";
				} else {
					pop = parts.pop();
				}

				if ( pop == null ) {
					pop = context;
				}

				Expr.relative[ cur ]( checkSet, pop, contextXML );
			}

		} else {
			checkSet = parts = [];
		}
	}

	if ( !checkSet ) {
		checkSet = set;
	}

	if ( !checkSet ) {
		Sizzle.error( cur || selector );
	}

	if ( toString.call(checkSet) === "[object Array]" ) {
		if ( !prune ) {
			results.push.apply( results, checkSet );

		} else if ( context && context.nodeType === 1 ) {
			for ( i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}

		} else {
			for ( i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
					results.push( set[i] );
				}
			}
		}

	} else {
		makeArray( checkSet, results );
	}

	if ( extra ) {
		Sizzle( extra, origContext, results, seed );
		Sizzle.uniqueSort( results );
	}

	return results;
};

Sizzle.uniqueSort = function( results ) {
	if ( sortOrder ) {
		hasDuplicate = baseHasDuplicate;
		results.sort( sortOrder );

		if ( hasDuplicate ) {
			for ( var i = 1; i < results.length; i++ ) {
				if ( results[i] === results[ i - 1 ] ) {
					results.splice( i--, 1 );
				}
			}
		}
	}

	return results;
};

Sizzle.matches = function( expr, set ) {
	return Sizzle( expr, null, null, set );
};

Sizzle.matchesSelector = function( node, expr ) {
	return Sizzle( expr, null, null, [node] ).length > 0;
};

Sizzle.find = function( expr, context, isXML ) {
	var set;

	if ( !expr ) {
		return [];
	}

	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
		var match,
			type = Expr.order[i];
		
		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
			var left = match[1];
			match.splice( 1, 1 );

			if ( left.substr( left.length - 1 ) !== "\\" ) {
				match[1] = (match[1] || "").replace( rBackslash, "" );
				set = Expr.find[ type ]( match, context, isXML );

				if ( set != null ) {
					expr = expr.replace( Expr.match[ type ], "" );
					break;
				}
			}
		}
	}

	if ( !set ) {
		set = typeof context.getElementsByTagName !== "undefined" ?
			context.getElementsByTagName( "*" ) :
			[];
	}

	return { set: set, expr: expr };
};

Sizzle.filter = function( expr, set, inplace, not ) {
	var match, anyFound,
		old = expr,
		result = [],
		curLoop = set,
		isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );

	while ( expr && set.length ) {
		for ( var type in Expr.filter ) {
			if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
				var found, item,
					filter = Expr.filter[ type ],
					left = match[1];

				anyFound = false;

				match.splice(1,1);

				if ( left.substr( left.length - 1 ) === "\\" ) {
					continue;
				}

				if ( curLoop === result ) {
					result = [];
				}

				if ( Expr.preFilter[ type ] ) {
					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );

					if ( !match ) {
						anyFound = found = true;

					} else if ( match === true ) {
						continue;
					}
				}

				if ( match ) {
					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
						if ( item ) {
							found = filter( item, match, i, curLoop );
							var pass = not ^ !!found;

							if ( inplace && found != null ) {
								if ( pass ) {
									anyFound = true;

								} else {
									curLoop[i] = false;
								}

							} else if ( pass ) {
								result.push( item );
								anyFound = true;
							}
						}
					}
				}

				if ( found !== undefined ) {
					if ( !inplace ) {
						curLoop = result;
					}

					expr = expr.replace( Expr.match[ type ], "" );

					if ( !anyFound ) {
						return [];
					}

					break;
				}
			}
		}

		// Improper expression
		if ( expr === old ) {
			if ( anyFound == null ) {
				Sizzle.error( expr );

			} else {
				break;
			}
		}

		old = expr;
	}

	return curLoop;
};

Sizzle.error = function( msg ) {
	throw "Syntax error, unrecognized expression: " + msg;
};

var Expr = Sizzle.selectors = {
	order: [ "ID", "NAME", "TAG" ],

	match: {
		ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
		CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
		TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
		CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
		PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
	},

	leftMatch: {},

	attrMap: {
		"class": "className",
		"for": "htmlFor"
	},

	attrHandle: {
		href: function( elem ) {
			return elem.getAttribute( "href" );
		},
		type: function( elem ) {
			return elem.getAttribute( "type" );
		}
	},

	relative: {
		"+": function(checkSet, part){
			var isPartStr = typeof part === "string",
				isTag = isPartStr && !rNonWord.test( part ),
				isPartStrNotTag = isPartStr && !isTag;

			if ( isTag ) {
				part = part.toLowerCase();
			}

			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
				if ( (elem = checkSet[i]) ) {
					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

					checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
						elem || false :
						elem === part;
				}
			}

			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},

		">": function( checkSet, part ) {
			var elem,
				isPartStr = typeof part === "string",
				i = 0,
				l = checkSet.length;

			if ( isPartStr && !rNonWord.test( part ) ) {
				part = part.toLowerCase();

				for ( ; i < l; i++ ) {
					elem = checkSet[i];

					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
					}
				}

			} else {
				for ( ; i < l; i++ ) {
					elem = checkSet[i];

					if ( elem ) {
						checkSet[i] = isPartStr ?
							elem.parentNode :
							elem.parentNode === part;
					}
				}

				if ( isPartStr ) {
					Sizzle.filter( part, checkSet, true );
				}
			}
		},

		"": function(checkSet, part, isXML){
			var nodeCheck,
				doneName = done++,
				checkFn = dirCheck;

			if ( typeof part === "string" && !rNonWord.test( part ) ) {
				part = part.toLowerCase();
				nodeCheck = part;
				checkFn = dirNodeCheck;
			}

			checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
		},

		"~": function( checkSet, part, isXML ) {
			var nodeCheck,
				doneName = done++,
				checkFn = dirCheck;

			if ( typeof part === "string" && !rNonWord.test( part ) ) {
				part = part.toLowerCase();
				nodeCheck = part;
				checkFn = dirNodeCheck;
			}

			checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
		}
	},

	find: {
		ID: function( match, context, isXML ) {
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				// Check parentNode to catch when Blackberry 4.6 returns
				// nodes that are no longer in the document #6963
				return m && m.parentNode ? [m] : [];
			}
		},

		NAME: function( match, context ) {
			if ( typeof context.getElementsByName !== "undefined" ) {
				var ret = [],
					results = context.getElementsByName( match[1] );

				for ( var i = 0, l = results.length; i < l; i++ ) {
					if ( results[i].getAttribute("name") === match[1] ) {
						ret.push( results[i] );
					}
				}

				return ret.length === 0 ? null : ret;
			}
		},

		TAG: function( match, context ) {
			if ( typeof context.getElementsByTagName !== "undefined" ) {
				return context.getElementsByTagName( match[1] );
			}
		}
	},
	preFilter: {
		CLASS: function( match, curLoop, inplace, result, not, isXML ) {
			match = " " + match[1].replace( rBackslash, "" ) + " ";

			if ( isXML ) {
				return match;
			}

			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
				if ( elem ) {
					if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
						if ( !inplace ) {
							result.push( elem );
						}

					} else if ( inplace ) {
						curLoop[i] = false;
					}
				}
			}

			return false;
		},

		ID: function( match ) {
			return match[1].replace( rBackslash, "" );
		},

		TAG: function( match, curLoop ) {
			return match[1].replace( rBackslash, "" ).toLowerCase();
		},

		CHILD: function( match ) {
			if ( match[1] === "nth" ) {
				if ( !match[2] ) {
					Sizzle.error( match[0] );
				}

				match[2] = match[2].replace(/^\+|\s*/g, '');

				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
				var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
					match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

				// calculate the numbers (first)n+(last) including if they are negative
				match[2] = (test[1] + (test[2] || 1)) - 0;
				match[3] = test[3] - 0;
			}
			else if ( match[2] ) {
				Sizzle.error( match[0] );
			}

			// TODO: Move to normal caching system
			match[0] = done++;

			return match;
		},

		ATTR: function( match, curLoop, inplace, result, not, isXML ) {
			var name = match[1] = match[1].replace( rBackslash, "" );
			
			if ( !isXML && Expr.attrMap[name] ) {
				match[1] = Expr.attrMap[name];
			}

			// Handle if an un-quoted value was used
			match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );

			if ( match[2] === "~=" ) {
				match[4] = " " + match[4] + " ";
			}

			return match;
		},

		PSEUDO: function( match, curLoop, inplace, result, not ) {
			if ( match[1] === "not" ) {
				// If we're dealing with a complex expression, or a simple one
				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
					match[3] = Sizzle(match[3], null, null, curLoop);

				} else {
					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);

					if ( !inplace ) {
						result.push.apply( result, ret );
					}

					return false;
				}

			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
				return true;
			}
			
			return match;
		},

		POS: function( match ) {
			match.unshift( true );

			return match;
		}
	},
	
	filters: {
		enabled: function( elem ) {
			return elem.disabled === false && elem.type !== "hidden";
		},

		disabled: function( elem ) {
			return elem.disabled === true;
		},

		checked: function( elem ) {
			return elem.checked === true;
		},
		
		selected: function( elem ) {
			// Accessing this property makes selected-by-default
			// options in Safari work properly
			if ( elem.parentNode ) {
				elem.parentNode.selectedIndex;
			}
			
			return elem.selected === true;
		},

		parent: function( elem ) {
			return !!elem.firstChild;
		},

		empty: function( elem ) {
			return !elem.firstChild;
		},

		has: function( elem, i, match ) {
			return !!Sizzle( match[3], elem ).length;
		},

		header: function( elem ) {
			return (/h\d/i).test( elem.nodeName );
		},

		text: function( elem ) {
			var attr = elem.getAttribute( "type" ), type = elem.type;
			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) 
			// use getAttribute instead to test this case
			return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
		},

		radio: function( elem ) {
			return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
		},

		checkbox: function( elem ) {
			return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
		},

		file: function( elem ) {
			return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
		},

		password: function( elem ) {
			return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
		},

		submit: function( elem ) {
			var name = elem.nodeName.toLowerCase();
			return (name === "input" || name === "button") && "submit" === elem.type;
		},

		image: function( elem ) {
			return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
		},

		reset: function( elem ) {
			var name = elem.nodeName.toLowerCase();
			return (name === "input" || name === "button") && "reset" === elem.type;
		},

		button: function( elem ) {
			var name = elem.nodeName.toLowerCase();
			return name === "input" && "button" === elem.type || name === "button";
		},

		input: function( elem ) {
			return (/input|select|textarea|button/i).test( elem.nodeName );
		},

		focus: function( elem ) {
			return elem === elem.ownerDocument.activeElement;
		}
	},
	setFilters: {
		first: function( elem, i ) {
			return i === 0;
		},

		last: function( elem, i, match, array ) {
			return i === array.length - 1;
		},

		even: function( elem, i ) {
			return i % 2 === 0;
		},

		odd: function( elem, i ) {
			return i % 2 === 1;
		},

		lt: function( elem, i, match ) {
			return i < match[3] - 0;
		},

		gt: function( elem, i, match ) {
			return i > match[3] - 0;
		},

		nth: function( elem, i, match ) {
			return match[3] - 0 === i;
		},

		eq: function( elem, i, match ) {
			return match[3] - 0 === i;
		}
	},
	filter: {
		PSEUDO: function( elem, match, i, array ) {
			var name = match[1],
				filter = Expr.filters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );

			} else if ( name === "contains" ) {
				return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;

			} else if ( name === "not" ) {
				var not = match[3];

				for ( var j = 0, l = not.length; j < l; j++ ) {
					if ( not[j] === elem ) {
						return false;
					}
				}

				return true;

			} else {
				Sizzle.error( name );
			}
		},

		CHILD: function( elem, match ) {
			var type = match[1],
				node = elem;

			switch ( type ) {
				case "only":
				case "first":
					while ( (node = node.previousSibling) )	 {
						if ( node.nodeType === 1 ) { 
							return false; 
						}
					}

					if ( type === "first" ) { 
						return true; 
					}

					node = elem;

				case "last":
					while ( (node = node.nextSibling) )	 {
						if ( node.nodeType === 1 ) { 
							return false; 
						}
					}

					return true;

				case "nth":
					var first = match[2],
						last = match[3];

					if ( first === 1 && last === 0 ) {
						return true;
					}
					
					var doneName = match[0],
						parent = elem.parentNode;
	
					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
						var count = 0;
						
						for ( node = parent.firstChild; node; node = node.nextSibling ) {
							if ( node.nodeType === 1 ) {
								node.nodeIndex = ++count;
							}
						} 

						parent.sizcache = doneName;
					}
					
					var diff = elem.nodeIndex - last;

					if ( first === 0 ) {
						return diff === 0;

					} else {
						return ( diff % first === 0 && diff / first >= 0 );
					}
			}
		},

		ID: function( elem, match ) {
			return elem.nodeType === 1 && elem.getAttribute("id") === match;
		},

		TAG: function( elem, match ) {
			return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
		},
		
		CLASS: function( elem, match ) {
			return (" " + (elem.className || elem.getAttribute("class")) + " ")
				.indexOf( match ) > -1;
		},

		ATTR: function( elem, match ) {
			var name = match[1],
				result = Expr.attrHandle[ name ] ?
					Expr.attrHandle[ name ]( elem ) :
					elem[ name ] != null ?
						elem[ name ] :
						elem.getAttribute( name ),
				value = result + "",
				type = match[2],
				check = match[4];

			return result == null ?
				type === "!=" :
				type === "=" ?
				value === check :
				type === "*=" ?
				value.indexOf(check) >= 0 :
				type === "~=" ?
				(" " + value + " ").indexOf(check) >= 0 :
				!check ?
				value && result !== false :
				type === "!=" ?
				value !== check :
				type === "^=" ?
				value.indexOf(check) === 0 :
				type === "$=" ?
				value.substr(value.length - check.length) === check :
				type === "|=" ?
				value === check || value.substr(0, check.length + 1) === check + "-" :
				false;
		},

		POS: function( elem, match, i, array ) {
			var name = match[2],
				filter = Expr.setFilters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			}
		}
	}
};

var origPOS = Expr.match.POS,
	fescape = function(all, num){
		return "\\" + (num - 0 + 1);
	};

for ( var type in Expr.match ) {
	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
}

var makeArray = function( array, results ) {
	array = Array.prototype.slice.call( array, 0 );

	if ( results ) {
		results.push.apply( results, array );
		return results;
	}
	
	return array;
};

// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
// Also verifies that the returned array holds DOM nodes
// (which is not the case in the Blackberry browser)
try {
	Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;

// Provide a fallback method if it does not work
} catch( e ) {
	makeArray = function( array, results ) {
		var i = 0,
			ret = results || [];

		if ( toString.call(array) === "[object Array]" ) {
			Array.prototype.push.apply( ret, array );

		} else {
			if ( typeof array.length === "number" ) {
				for ( var l = array.length; i < l; i++ ) {
					ret.push( array[i] );
				}

			} else {
				for ( ; array[i]; i++ ) {
					ret.push( array[i] );
				}
			}
		}

		return ret;
	};
}

var sortOrder, siblingCheck;

if ( document.documentElement.compareDocumentPosition ) {
	sortOrder = function( a, b ) {
		if ( a === b ) {
			hasDuplicate = true;
			return 0;
		}

		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
			return a.compareDocumentPosition ? -1 : 1;
		}

		return a.compareDocumentPosition(b) & 4 ? -1 : 1;
	};

} else {
	sortOrder = function( a, b ) {
		// The nodes are identical, we can exit early
		if ( a === b ) {
			hasDuplicate = true;
			return 0;

		// Fallback to using sourceIndex (in IE) if it's available on both nodes
		} else if ( a.sourceIndex && b.sourceIndex ) {
			return a.sourceIndex - b.sourceIndex;
		}

		var al, bl,
			ap = [],
			bp = [],
			aup = a.parentNode,
			bup = b.parentNode,
			cur = aup;

		// If the nodes are siblings (or identical) we can do a quick check
		if ( aup === bup ) {
			return siblingCheck( a, b );

		// If no parents were found then the nodes are disconnected
		} else if ( !aup ) {
			return -1;

		} else if ( !bup ) {
			return 1;
		}

		// Otherwise they're somewhere else in the tree so we need
		// to build up a full list of the parentNodes for comparison
		while ( cur ) {
			ap.unshift( cur );
			cur = cur.parentNode;
		}

		cur = bup;

		while ( cur ) {
			bp.unshift( cur );
			cur = cur.parentNode;
		}

		al = ap.length;
		bl = bp.length;

		// Start walking down the tree looking for a discrepancy
		for ( var i = 0; i < al && i < bl; i++ ) {
			if ( ap[i] !== bp[i] ) {
				return siblingCheck( ap[i], bp[i] );
			}
		}

		// We ended someplace up the tree so do a sibling check
		return i === al ?
			siblingCheck( a, bp[i], -1 ) :
			siblingCheck( ap[i], b, 1 );
	};

	siblingCheck = function( a, b, ret ) {
		if ( a === b ) {
			return ret;
		}

		var cur = a.nextSibling;

		while ( cur ) {
			if ( cur === b ) {
				return -1;
			}

			cur = cur.nextSibling;
		}

		return 1;
	};
}

// Utility function for retreiving the text value of an array of DOM nodes
Sizzle.getText = function( elems ) {
	var ret = "", elem;

	for ( var i = 0; elems[i]; i++ ) {
		elem = elems[i];

		// Get the text from text nodes and CDATA nodes
		if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
			ret += elem.nodeValue;

		// Traverse everything else, except comment nodes
		} else if ( elem.nodeType !== 8 ) {
			ret += Sizzle.getText( elem.childNodes );
		}
	}

	return ret;
};

// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
	// We're going to inject a fake input element with a specified name
	var form = document.createElement("div"),
		id = "script" + (new Date()).getTime(),
		root = document.documentElement;

	form.innerHTML = "<a name='" + id + "'/>";

	// Inject it into the root element, check its status, and remove it quickly
	root.insertBefore( form, root.firstChild );

	// The workaround has to do additional checks after a getElementById
	// Which slows things down for other browsers (hence the branching)
	if ( document.getElementById( id ) ) {
		Expr.find.ID = function( match, context, isXML ) {
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);

				return m ?
					m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
						[m] :
						undefined :
					[];
			}
		};

		Expr.filter.ID = function( elem, match ) {
			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");

			return elem.nodeType === 1 && node && node.nodeValue === match;
		};
	}

	root.removeChild( form );

	// release memory in IE
	root = form = null;
})();

(function(){
	// Check to see if the browser returns only elements
	// when doing getElementsByTagName("*")

	// Create a fake element
	var div = document.createElement("div");
	div.appendChild( document.createComment("") );

	// Make sure no comments are found
	if ( div.getElementsByTagName("*").length > 0 ) {
		Expr.find.TAG = function( match, context ) {
			var results = context.getElementsByTagName( match[1] );

			// Filter out possible comments
			if ( match[1] === "*" ) {
				var tmp = [];

				for ( var i = 0; results[i]; i++ ) {
					if ( results[i].nodeType === 1 ) {
						tmp.push( results[i] );
					}
				}

				results = tmp;
			}

			return results;
		};
	}

	// Check to see if an attribute returns normalized href attributes
	div.innerHTML = "<a href='#'></a>";

	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
			div.firstChild.getAttribute("href") !== "#" ) {

		Expr.attrHandle.href = function( elem ) {
			return elem.getAttribute( "href", 2 );
		};
	}

	// release memory in IE
	div = null;
})();

if ( document.querySelectorAll ) {
	(function(){
		var oldSizzle = Sizzle,
			div = document.createElement("div"),
			id = "__sizzle__";

		div.innerHTML = "<p class='TEST'></p>";

		// Safari can't handle uppercase or unicode characters when
		// in quirks mode.
		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
			return;
		}
	
		Sizzle = function( query, context, extra, seed ) {
			context = context || document;

			// Only use querySelectorAll on non-XML documents
			// (ID selectors don't work in non-HTML documents)
			if ( !seed && !Sizzle.isXML(context) ) {
				// See if we find a selector to speed up
				var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
				
				if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
					// Speed-up: Sizzle("TAG")
					if ( match[1] ) {
						return makeArray( context.getElementsByTagName( query ), extra );
					
					// Speed-up: Sizzle(".CLASS")
					} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
						return makeArray( context.getElementsByClassName( match[2] ), extra );
					}
				}
				
				if ( context.nodeType === 9 ) {
					// Speed-up: Sizzle("body")
					// The body element only exists once, optimize finding it
					if ( query === "body" && context.body ) {
						return makeArray( [ context.body ], extra );
						
					// Speed-up: Sizzle("#ID")
					} else if ( match && match[3] ) {
						var elem = context.getElementById( match[3] );

						// Check parentNode to catch when Blackberry 4.6 returns
						// nodes that are no longer in the document #6963
						if ( elem && elem.parentNode ) {
							// Handle the case where IE and Opera return items
							// by name instead of ID
							if ( elem.id === match[3] ) {
								return makeArray( [ elem ], extra );
							}
							
						} else {
							return makeArray( [], extra );
						}
					}
					
					try {
						return makeArray( context.querySelectorAll(query), extra );
					} catch(qsaError) {}

				// qSA works strangely on Element-rooted queries
				// We can work around this by specifying an extra ID on the root
				// and working up from there (Thanks to Andrew Dupont for the technique)
				// IE 8 doesn't work on object elements
				} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
					var oldContext = context,
						old = context.getAttribute( "id" ),
						nid = old || id,
						hasParent = context.parentNode,
						relativeHierarchySelector = /^\s*[+~]/.test( query );

					if ( !old ) {
						context.setAttribute( "id", nid );
					} else {
						nid = nid.replace( /'/g, "\\$&" );
					}
					if ( relativeHierarchySelector && hasParent ) {
						context = context.parentNode;
					}

					try {
						if ( !relativeHierarchySelector || hasParent ) {
							return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
						}

					} catch(pseudoError) {
					} finally {
						if ( !old ) {
							oldContext.removeAttribute( "id" );
						}
					}
				}
			}
		
			return oldSizzle(query, context, extra, seed);
		};

		for ( var prop in oldSizzle ) {
			Sizzle[ prop ] = oldSizzle[ prop ];
		}

		// release memory in IE
		div = null;
	})();
}

(function(){
	var html = document.documentElement,
		matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;

	if ( matches ) {
		// Check to see if it's possible to do matchesSelector
		// on a disconnected node (IE 9 fails this)
		var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
			pseudoWorks = false;

		try {
			// This should fail with an exception
			// Gecko does not error, returns false instead
			matches.call( document.documentElement, "[test!='']:sizzle" );
	
		} catch( pseudoError ) {
			pseudoWorks = true;
		}

		Sizzle.matchesSelector = function( node, expr ) {
			// Make sure that attribute selectors are quoted
			expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");

			if ( !Sizzle.isXML( node ) ) {
				try { 
					if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
						var ret = matches.call( node, expr );

						// IE 9's matchesSelector returns false on disconnected nodes
						if ( ret || !disconnectedMatch ||
								// As well, disconnected nodes are said to be in a document
								// fragment in IE 9, so check for that
								node.document && node.document.nodeType !== 11 ) {
							return ret;
						}
					}
				} catch(e) {}
			}

			return Sizzle(expr, null, null, [node]).length > 0;
		};
	}
})();

(function(){
	var div = document.createElement("div");

	div.innerHTML = "<div class='test e'></div><div class='test'></div>";

	// Opera can't find a second classname (in 9.6)
	// Also, make sure that getElementsByClassName actually exists
	if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
		return;
	}

	// Safari caches class attributes, doesn't catch changes (in 3.2)
	div.lastChild.className = "e";

	if ( div.getElementsByClassName("e").length === 1 ) {
		return;
	}
	
	Expr.order.splice(1, 0, "CLASS");
	Expr.find.CLASS = function( match, context, isXML ) {
		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
			return context.getElementsByClassName(match[1]);
		}
	};

	// release memory in IE
	div = null;
})();

function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];

		if ( elem ) {
			var match = false;

			elem = elem[dir];

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 && !isXML ){
					elem.sizcache = doneName;
					elem.sizset = i;
				}

				if ( elem.nodeName.toLowerCase() === cur ) {
					match = elem;
					break;
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];

		if ( elem ) {
			var match = false;
			
			elem = elem[dir];

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 ) {
					if ( !isXML ) {
						elem.sizcache = doneName;
						elem.sizset = i;
					}

					if ( typeof cur !== "string" ) {
						if ( elem === cur ) {
							match = true;
							break;
						}

					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
						match = elem;
						break;
					}
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

if ( document.documentElement.contains ) {
	Sizzle.contains = function( a, b ) {
		return a !== b && (a.contains ? a.contains(b) : true);
	};

} else if ( document.documentElement.compareDocumentPosition ) {
	Sizzle.contains = function( a, b ) {
		return !!(a.compareDocumentPosition(b) & 16);
	};

} else {
	Sizzle.contains = function() {
		return false;
	};
}

Sizzle.isXML = function( elem ) {
	// documentElement is verified for cases where it doesn't yet exist
	// (such as loading iframes in IE - #4833) 
	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;

	return documentElement ? documentElement.nodeName !== "HTML" : false;
};

var posProcess = function( selector, context ) {
	var match,
		tmpSet = [],
		later = "",
		root = context.nodeType ? [context] : context;

	// Position selectors must be done after the filter
	// And so must :not(positional) so we move all PSEUDOs to the end
	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
		later += match[0];
		selector = selector.replace( Expr.match.PSEUDO, "" );
	}

	selector = Expr.relative[selector] ? selector + "*" : selector;

	for ( var i = 0, l = root.length; i < l; i++ ) {
		Sizzle( selector, root[i], tmpSet );
	}

	return Sizzle.filter( later, tmpSet );
};

//--------------------------------------------------------------------------------
//--------------------END Sizzle -------------------------------------------------
//--------------------------------------------------------------------------------








})(
    //try to get a reference to the global object, regardless of the scope the code is executed in
    //kangax - http://perfectionkills.com/
    (function(){ return this || (1,eval)('this') })()
  );
